/**
 * 2017年11月23日
 */
package exp.algorithm.sic.weight;


import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.stream.Collectors;

import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;

import exp.algorithm.sic.InstanceFeatureVectors;
import exp.algorithm.sic.PeakFinderType;
import exp.algorithm.sic.SiftConverter;
import exp.algorithm.sic.VectorMakerType;
import exp.algorithm.sic.feature.FeatureVector;
import exp.algorithm.sic.feature.LabeledDenseFeatureVector;
import exp.algorithm.sic.feature.LabeledFeatureVector;
import weka.classifiers.AbstractClassifier;
import weka.core.Attribute;
import weka.core.Instance;
import weka.core.Instances;

/**
 * @author Alex
 *
 */
public class ReliefFWeight2 extends AbstractClassifier {
	int k = 5;
	interface FVCB{
		void doWhat(FeatureVector fv,double classVal,int seq,int totalSeq);
	}
	class ResultHolder{
		public Object value;
	}
	static class FeatureVectorHolder{
		public int i;
		public int j;
		public FeatureVector fv;
		public double classValue;
		public FeatureVectorHolder(FeatureVector fv,int i ,int j,double classValue) {
			this.i = i;
			this.j = j;
			this.fv = fv;
			this.classValue = classValue;
		}
	}
	double [][] weights;
	int []classValues ;
	double dist[][];
	//classFeatureVal是类标数目
	public void input(List<InstanceFeatureVectors> vecs,int classAttNum,Attribute classAttribute,double[]prioris){
		int k =0;
		for(int i =0;i<vecs.size();i++){
			InstanceFeatureVectors ifv = vecs.get(i);
			k+=ifv.vecs.size();
		}
		//共k个特征向量
		this.weights= new double[k][];
		this.classValues = new int[weights.length];
		int instNum = vecs.size();
		//这是一个上三角阵,下三角没用
		this.dist = new double[k][k];
		//利用这个indexList计算dist矩阵的值,dist[i][j]表示indexList中的第i个与第j个的距离是dist[i][j];
		List<FeatureVectorHolder> indexList = new ArrayList<FeatureVectorHolder>(k);
		int sq  = 0;
		for(int i = 0 ; i< instNum;i++){
			InstanceFeatureVectors ifv = vecs.get(i);
			double classValue = ifv.classValue;
			for(int j=0;j<ifv.vecs.size();j++){
				//该向量是属性训练集中第i个实例中的第j个向量,第i个实例的类标值是classValue
				indexList.add(new FeatureVectorHolder(ifv.vecs.get(j), i, j, classValue));
				classValues[sq] = (int)classValue;
				sq++;
			}
		}
		for(int i = 0;i<k;i++){
			dist[i][i] = 0.0;
			for(int j = i+1;j<k;j++){
				dist[i][j] = distance(indexList.get(i),indexList.get(j));
			}
		}
		
		
		sq  = 0;
		for(int i = 0 ; i< instNum;i++){
			InstanceFeatureVectors ifv = vecs.get(i);
			double classValue = ifv.classValue;
			for(int j=0;j<ifv.vecs.size();j++){
				FeatureVector fv = ifv.vecs.get(j);
				List<FeatureVector> kHits = findKHit(vecs,ifv,fv,sq);
				List<FeatureVector> kMisses = findKMiss(vecs,ifv,fv,sq);
				double weight[] = new double[classAttNum];
				weight[(int)classValue] = calc(fv,kHits ,kMisses,prioris);
				this.weights[sq] = weight;
				//this.classValue[sq] = (int)classValue;
				sq++;
			}
		}
	}
	public double[][] getWeight(){
		return this.weights;
	}
	
	public int[] correspondingVectorClassValue(){
	//	return this.classValue;
		return null;
	}
	
	private double distance(FeatureVectorHolder fvh1, FeatureVectorHolder fvh2){
		return distance(fvh1.fv, fvh2.fv);
	}
	
	private double kHitsWeight(FeatureVector fv,List<FeatureVector> kHits){
		double sum = 0;
		for(FeatureVector fve:kHits){
			sum -= distance(fv, fve);
		}
		return sum;
	}
	private double kMissWeight(FeatureVector fv,List<FeatureVector> kMisses,double[]prioris){
		double sum = 0;
		for(int i =0;i<kMisses.size();i++){
			LabeledFeatureVector lfve = (LabeledFeatureVector)kMisses.get(i);
			sum += prioris[(int)lfve.getClassValue()] * distance(fv, lfve);
		}
		return sum;
	}
	
	private double calc(FeatureVector fv,List<FeatureVector> kHits,List<FeatureVector> kMisses,double[]prioris){
		return kHitsWeight(fv, kHits)+kMissWeight(fv, kMisses,prioris);
	}
	
	void iteratorFeatrueVector(List<InstanceFeatureVectors> vecs,FVCB fvcb){
		int instNum = vecs.size();
		int seq = 0;
		for(int i = 0 ; i< instNum;i++){
			InstanceFeatureVectors ifv = vecs.get(i);
			double classValue = ifv.classValue;
			for(int j=0;j<ifv.vecs.size();j++){
				FeatureVector fv = ifv.vecs.get(j);
				fvcb.doWhat(fv, classValue, j, seq);
				seq ++ ;
			}
		}
	}
	/**
	 * 
	 * @param vecs
	 * @param v
	 * @param fv
	 * @param sequence fv的序号
	 * @return
	 */
	List<FeatureVector> findKHit(List<InstanceFeatureVectors> vecs,InstanceFeatureVectors v,FeatureVector fv,int sequence){
		return doK(vecs, v, fv, sequence, true);
		
	}
	@SuppressWarnings("unchecked")
	private List<FeatureVector> doK(List<InstanceFeatureVectors> vecs,InstanceFeatureVectors v,FeatureVector fv,int sequence,boolean sameClass){
		double cv = v.classValue;
		final List<FeatureVector> list = new ArrayList<>();
		iteratorFeatrueVector(vecs, (FeatureVector vec,double classVal,int seq,int totalSeq)->{
			if(sameClass){
				if(classVal == cv && seq != sequence ){
					list.add(vec);
				}
			}else{
				if(classVal != cv && seq != sequence ){
					list.add(new LabeledDenseFeatureVector(vec,classVal));
				}
			}
				
		});
		final Pair<FeatureVector,Double>[] res = new Pair[list.size()];
		for(int i=0;i<res.length;i++){
			res[i] = new ImmutablePair<FeatureVector, Double>(list.get(i),distance(list.get(i), fv));
		}
	
		Arrays.sort(res,(x,y)->{
			return x.getRight().compareTo(y.getRight());
		});
		int max = Math.min(this.k,res.length);
		return Arrays.asList(res).stream().map(x->x.getLeft()).collect(Collectors.toList()).subList(0, max);
	}
	
	List<FeatureVector> findKMiss(List<InstanceFeatureVectors> vecs,InstanceFeatureVectors v,FeatureVector fv,int sequence){
		return doK(vecs, v, fv, sequence, false);
	}
	double distance(FeatureVector v1,FeatureVector v2){
		double sum = 0.0;
		for(int i = 0 ;i<v1.length();i++){
			if(!Double.isNaN(v1.getVal(i)) && !Double.isNaN(v2.getVal(i)))
				sum = sum + Math.sqrt(Math.abs(v1.getVal(i)*v1.getVal(i)-v2.getVal(i)*v2.getVal(i)));
		}
		return Math.sqrt(sum);
	}
	
	SiftConverter sc = new SiftConverter(PeakFinderType.EXTREME,VectorMakerType.SHAPE);
	
	@Override
	public void buildClassifier(Instances data) throws Exception {
		double [] priors = calcPriors(data);
		sc.transform(data);
		input(sc.getInstanceVectors(), data.classAttribute().numValues(), data.classAttribute(),priors);
		
	}
	double[] calcPriors(Instances instances){
	
		double []m_Priors = new double[instances.numClasses()];
		
		Enumeration enumInsts = instances.enumerateInstances();
		    while (enumInsts.hasMoreElements()) {
		      Instance instance = (Instance) enumInsts.nextElement();
			      if (!instance.classIsMissing()) {
					m_Priors[(int)instance.classValue()]++;
			}
		}
		return m_Priors;
		    		
	}
	
	
}
